1 module unde.games.renderer;
2 
3 import std.format;
4 import std.conv;
5 import std.math;
6 import std.string;
7 import std.stdio;
8 
9 import derelict.sdl2.image;
10 import derelict.sdl2.sdl;
11 import derelict.opengl3.gl3;
12 import derelict.opengl3.gl;
13 import std.algorithm.comparison;
14 
15 import std.utf;
16 import unde.global_state;
17 import unde.games.obj_loader;
18 
19 private GLuint[string] textures;
20 
21 /* ---------------------------------------------------------------------------- */
22 bool apply_material(GlobalState gs, shared MtlMaterial *mtl, 
23     string delegate(GlobalState gs, string name) tex_anim = null)
24 {
25     float[4] c;
26 
27     GLenum fill_mode;
28     int ret1, ret2;
29     float opacity = 1.0;
30 
31     if (mtl.transparency) opacity = mtl.transparency;
32 
33     if (mtl.diffuse[0].isNaN())
34         c = [0.8f, 0.8f, 0.8f, opacity];
35     else
36         c = mtl.diffuse ~ opacity;
37     glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cast(const(float)*)c);
38 
39     //if (mtl.specular[0].isNaN())
40         c = [0.0f, 0.0f, 0.0f, 1.0f];
41     //else
42     //    c = mtl.specular ~ 1.0f;
43     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, cast(const(float)*)c);
44 
45     if (mtl.ambient[0].isNaN())
46 	c = [0.2f, 0.2f, 0.2f, 1.0f];
47     else
48         c = mtl.ambient ~ 1.0f;
49     glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, cast(const(float)*)c);
50 
51     if (mtl.emissive[0].isNaN())
52         c = [0.0f, 0.0f, 0.0f, 1.0f];
53     else
54         c = mtl.emissive ~ 1.0f;
55     glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, cast(const(float)*)c);
56 
57     glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
58     
59     glPolygonMode(GL_FRONT_AND_BACK, false ? GL_LINE : GL_FILL);
60 
61     //Not Two sided
62 	glEnable(GL_CULL_FACE);
63 
64     bool normals = false;
65     if(mtl.map_diffuse !is null)
66     {
67         string tex_name = mtl.map_diffuse;
68         if (tex_anim) tex_name = tex_anim(gs, tex_name);
69         string p = format("models/%s", tex_name);
70 
71         GLuint texture_name;
72         bool link;
73         if (p !in textures)
74         {
75             glGenTextures(1, &texture_name);// generate GL-textures ID's
76             link = true;
77         }
78         else texture_name = textures[p];
79         //writefln("texture_name=%s", texture_name);
80         glBindTexture(GL_TEXTURE_2D, texture_name);// Binding of texture name
81 
82         if (link)
83         {
84             //
85             //redefine standard texture values
86             //
87             // We will use linear interpolation for magnification filter
88             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
89             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
90             // tiling mode
91             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, true ? GL_REPEAT : GL_CLAMP);
92             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, true ? GL_REPEAT : GL_CLAMP);
93                                     
94             auto image = IMG_Load(p.toStringz);
95             if (!image)
96             {
97                 throw new Exception(format("Error while loading texture: %s", p));
98             }
99     
100             // Texture specification
101             glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
102             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.w, image.h, 0, GL_RGBA, GL_UNSIGNED_BYTE,
103                 image.pixels);
104             SDL_FreeSurface(image);
105 
106             textures[p] = texture_name;
107         }
108         return true;
109     }
110     else
111     {
112         glBindTexture(GL_TEXTURE_2D, 0);
113     }
114 
115     return false;
116 }
117 
118 GLuint[shared ObjMesh*] gl_lists;
119 
120 import core.runtime;
121 Throwable.TraceInfo getStackTrace() {
122 
123     version(Posix) {
124         // druntime cuts out the first few functions on the trace as they are internal
125         // so we'll make some dummy functions here so our actual info doesn't get cut
126         Throwable.TraceInfo f5() { return defaultTraceHandler(); }
127         Throwable.TraceInfo f4() { return f5(); }
128         Throwable.TraceInfo f3() { return f4(); }
129         Throwable.TraceInfo f2() { return f3(); }
130         auto stuff = f2();
131     } else {
132         auto stuff = defaultTraceHandler();
133     }
134 
135     return stuff;
136 }
137 
138 
139 /* ---------------------------------------------------------------------------- */
140 void recursive_render (GlobalState gs, shared ObjFile *sc,
141     bool delegate(GlobalState gs, string name) anim = null,
142     string delegate(GlobalState gs, string name) tex_anim = null,
143     bool dontcache = false, bool reverse = false)
144 {
145     //writefln("%s", getStackTrace());
146     
147     foreach(i; 0..sc.objects.length)
148     {
149         shared ObjObject *object;
150         if (!reverse) object = sc.objects[i];
151         else object = sc.objects[sc.objects.length-1-i];
152 
153         GLuint prev_tex_id_idx = 0;
154         
155         glPushMatrix();
156     
157         bool draw = true;
158         string name = object.name;
159         if (anim) draw = anim(gs, name);
160     
161         if (draw)
162         {
163             /* draw all meshes assigned to this node */
164             foreach (mesh; object.meshes) {
165                 //writefln("mesh %d. material %d", n, mesh.mMaterialIndex);
166                 bool is_texture = apply_material(gs, sc.mtl.materials[mesh.material], tex_anim);
167         
168                 if(object.normals is null) {
169                     glDisable(GL_LIGHTING);
170                 } else {
171                     glEnable(GL_LIGHTING);
172                 }
173                 
174                 glDisable(GL_COLOR_MATERIAL);
175     
176                 bool full_draw;
177                 if (!tex_anim && !dontcache)
178                 {
179                     if (mesh in gl_lists)
180                     {
181                         glCallList(gl_lists[mesh]);
182                     }
183                     else
184                     {
185                         gl_lists[mesh] = glGenLists(1);
186                         if (gl_lists[mesh] <= 0)
187                             throw new Exception(format("Error while glGenLists: %s", gl_lists[mesh]));
188                         glNewList(gl_lists[mesh], GL_COMPILE_AND_EXECUTE);
189                         full_draw = true;
190                     }
191                 }
192                 else full_draw = true;
193         
194                 if (full_draw)
195                 {
196                     foreach (face; mesh.faces) {
197                         GLenum face_mode;
198             
199                         switch(face.length) {
200                             case 1: face_mode = GL_POINTS; break;
201                             case 2: face_mode = GL_LINES; break;
202                             case 3: face_mode = GL_TRIANGLES; break;
203                             default: face_mode = GL_POLYGON; break;
204                         }
205             
206                         glBegin(face_mode);
207             
208                         foreach(index; face) {
209                             //glColor4fv(0.0f,0.0f,0.0f,1.0f);
210             
211                             if (is_texture && index.tex >= 0)
212                                 glTexCoord2f(object.texcoords[index.tex][0], 1.0-object.texcoords[index.tex][1]);
213             
214                             if(index.normal >= 0)
215                             {
216                                 float[3] normal = [ object.normals[index.normal][0], 
217                                                    -object.normals[index.normal][1],
218                                                    -object.normals[index.normal][2]];
219                                 glNormal3fv(normal.ptr);
220                             }
221 
222                             if (index.vert >= 0)
223                             {
224                                 float[3] coords = [-object.vertices[index.vert][0],
225                                                     object.vertices[index.vert][1],
226                                                     object.vertices[index.vert][2]];
227                                 glVertex3fv(coords.ptr);
228                             }
229                         }
230             
231                         glEnd();
232                      }
233                 }
234         
235                 if (!tex_anim && !dontcache && full_draw)
236                 {
237                     glEndList();
238                 }
239             }
240         }
241         
242         glPopMatrix();
243     }
244 }
245 
246 void render_box (float[6] box)
247 {
248     glBegin(GL_POLYGON);
249     glVertex3f(box[0], box[1], box[2]);
250     glVertex3f(box[0], box[4], box[2]);
251     glVertex3f(box[3], box[4], box[2]);
252     glVertex3f(box[3], box[1], box[2]);
253     glEnd();
254 
255     glBegin(GL_POLYGON);
256     glVertex3f(box[0], box[1], box[5]);
257     glVertex3f(box[0], box[4], box[5]);
258     glVertex3f(box[3], box[4], box[5]);
259     glVertex3f(box[3], box[1], box[5]);
260     glEnd();
261 
262     glBegin(GL_POLYGON);
263     glVertex3f(box[0], box[1], box[2]);
264     glVertex3f(box[0], box[4], box[2]);
265     glVertex3f(box[0], box[4], box[5]);
266     glVertex3f(box[0], box[1], box[5]);
267     glEnd();
268 
269     glBegin(GL_POLYGON);
270     glVertex3f(box[3], box[1], box[2]);
271     glVertex3f(box[3], box[4], box[2]);
272     glVertex3f(box[3], box[4], box[5]);
273     glVertex3f(box[3], box[1], box[5]);
274     glEnd();
275 
276     glBegin(GL_POLYGON);
277     glVertex3f(box[0], box[1], box[2]);
278     glVertex3f(box[3], box[1], box[2]);
279     glVertex3f(box[3], box[1], box[5]);
280     glVertex3f(box[0], box[1], box[5]);
281     glEnd();
282 
283     glBegin(GL_POLYGON);
284     glVertex3f(box[0], box[4], box[2]);
285     glVertex3f(box[3], box[4], box[2]);
286     glVertex3f(box[3], box[4], box[5]);
287     glVertex3f(box[0], box[4], box[5]);
288     glEnd();
289 }
290 
291 GLuint load_texture(string path, bool grayscale = false, int skiplines=0)
292 {
293     GLuint texture_name;
294     glGenTextures(1, &texture_name);// generate GL-textures ID's
295     glBindTexture(GL_TEXTURE_2D, texture_name);// Binding of texture name
296 
297     //
298     //redefine standard texture values
299     //
300     // We will use linear interpolation for magnification filter
301     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
302     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
303     // tiling mode
304     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, true ? GL_REPEAT : GL_CLAMP);
305     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, true ? GL_REPEAT : GL_CLAMP);
306                             
307     auto image = IMG_Load(path.toStringz());
308     if (!image)
309     {
310         throw new Exception(format("Error while loading texture: %s", path));
311     }
312 
313     // Texture specification
314     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
315     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 
316         image.w, image.h-skiplines, 0, grayscale?GL_LUMINANCE:GL_RGBA, GL_UNSIGNED_BYTE,
317         image.pixels + skiplines*image.w);
318 
319     return texture_name;
320 }
321 
322 void print_text(string text)
323 {
324     string[] encoding = ["@"," ","!",`"`,"#","$","%","&","'","(",")","*","+",",","-",".",
325                          "/","0","1","2","3","4","5","6","7","8","9",":",";","<","=",">",
326                          "?","@","A","B","C","D","E","F","G","H","I","J","K","L","M","N",
327                          "O","P","Q","R","S","T","U","V","W","X","Y","Z","[",`\`,"]","^",
328                          "_","`","a","b","c","d","e","f","g","h","i","j","k","l","m","n",
329                          "o","p","q","r","s","t","u","v","w","x","y","z","{","|","}","~",
330                          " "," "," ","Ё"," "," "," "," "," "," "," "," "," "," "," "," ",
331                          " "," "," ","ё"," "," "," "," "," "," "," "," "," "," "," "," ",
332                          "ю","а","б","ц","д","е","ф","г","х","и","й","к","л","м","н","о",
333                          "п","я","р","с","т","у","ж","в","ь","ы","з","ш","э","щ","ч","ъ",
334                          "Ю","А","Б","Ц","Д","Е","Ф","Г","Х","И","Й","К","Л","М","Н","О",
335                          "П","Я","Р","С","Т","У","Ж","В","Ь","Ы","З","Ш","Э","Щ","Ч","Ъ"];
336 
337     size_t x = 0;
338     int y = 0;
339     for (size_t i=0; i < text.length; i+=text.stride(i))
340     {
341         string chr = text[i..i+text.stride(i)];
342 
343         if (chr == "\n")
344         {
345             y--;
346             x=0;
347             continue;
348         }
349         
350         size_t code = -1;
351         foreach(j, enc; encoding)
352         {
353             if (chr == enc)
354             {
355                 code = j;
356                 break;
357             }
358         }
359         
360         assert(code != -1, chr);
361         float cx = (code%16)/16.0;
362         float cy = (code/16)/12.0;
363 
364         glBegin(GL_POLYGON);
365         glTexCoord2f(cx, cy);
366         glVertex3f(x, y, -10.0);
367         glTexCoord2f(cx, cy+1.0/12);
368         glVertex3f(x, y-1.0, -10.0);
369         glTexCoord2f(cx+1.0/16, cy+1.0/12);
370         glVertex3f(x+1.0, y-1.0, -10.0);
371         glTexCoord2f(cx+1.0/16, cy);
372         glVertex3f(x+1.0, y, -10.0);
373         glEnd();
374 
375         x++;
376     }
377 }
378 
379